#ifndef __MCS__OBJECT__HPP
#define __MCS__OBJECT__HPP

#include "String.hpp"


/// Generic Java-style object
/**
In general case it is a bad practice to introduce absolute root for all class
hierarchies, but this approach is perfect for representing semantic values in
bison parser.

Multiple inheritance is not used, so intrusive reference counter can be used
seamlessly. Reference counting is done via retain() and release() methods.
Destructor for the Object class is made protected in order to prevent deletion
of objects omitting reference counting mechanism.

There is no smart garbage collector that is capable to resolve cyclic references,
so there is a risk of memory leaks due to cyclic references. In order to prevent
appearance of cyclic references keep in mind that smart pointer with reference
counting are used to reference from parent to child (e.g. from Block to Element),
and simple pointers for referencing backwards (e.g. from Element to Block). For
debugging cases of cyclic references all living instances of the Object class are
registered in the global table of instances. This table is asserted to be empty
after leaving main(). For proper work of this assertion no global/static variables
should reference any instances of the Object's subclasses.
*/
class Object
{
private:
	Object(Object const &);
	Object & operator=(Object const &);
protected:
	virtual ~Object()
	{
		instances_[globalIndex_] = instances_.back();
		instances_[globalIndex_]->globalIndex_ = globalIndex_;
		globalIndex_ = -1;
		instances_.pop_back();
	}
public:
	static bool allObjectsRemoved() { return instances_.empty(); }

	Object() 
		: refCounter_(0)
	{
		globalIndex_ = instances_.size();
		instances_.push_back(this);
	}

	void retain()
	{
		++refCounter_;
	}
	
	void release()
	{
		if(!--refCounter_) 
		{
			delete this;
		}
	}
private:
	class InstancesTable : public std::vector<Object*>
	{
	public:
		~InstancesTable()
		{
			assert(empty());
		}
	};

	/// Reference counter, initially zero
	size_t refCounter_;
	/// Index in the instances table for fast removement from there.
	size_t globalIndex_;

	/// Global table of instances for debugging memory leaks due to cyclic references.
	/** Note: instantiated in Block.cpp */
	static InstancesTable instances_;
};

namespace detail
{
	template<class T> class trusted_cast_tag;

	class SafeBoolHelper
	{
	public:
		typedef void * (SafeBoolHelper::*XType)(int,bool,void*);
		XType first;
		XType second;
	};

	typedef SafeBoolHelper::XType (SafeBoolHelper::* SaveBool);
}

/// Smart pointer, that encapsulates reference counting.
template<class T> class ptr
{
public:
	ptr()
		: pObj_()
	{}
	
	ptr(T * obj)
		: pObj_(obj)
	{
		if(pObj_)
			pObj_->retain();
	}
	
	template<class Y> ptr(Y * obj)
		: pObj_(obj)
	{
		if(pObj_)
			pObj_->retain(); 
	}

	template<class Y> ptr(detail::trusted_cast_tag<Y*> const & );
	template<class Y> ptr(detail::trusted_cast_tag< ptr<Y> > const & );
	
	ptr(ptr<T> const & other)
		: pObj_(other.get())
	{
		if(pObj_)
			pObj_->retain();
	}

	template<class Y> ptr(ptr<Y> const & other)
		: pObj_(other.get())
	{
		if(pObj_)
			pObj_->retain();
	}

	~ptr()
	{
		if(pObj_)
			pObj_->release();
	}

	ptr<T> & operator=(T * other)
	{
		if(other) other->retain();
		T * tmp = pObj_;
		pObj_ = other;
		if(tmp) tmp->release();
		return *this;
	}

	ptr<T> & operator=(ptr<T> const & other)
	{
		return operator=(other.pObj_);
	}
	
	template<class Y> ptr<T> & operator=(ptr<Y> const & other)
	{
		return operator=(other.get());
	}
	
	T * operator->() const
	{
		return pObj_; 
	}
	
	bool operator!() const
	{
		return !pObj_; 
	}
	
	operator detail::SaveBool() const
	{
		return pObj_ ? &detail::SafeBoolHelper::second : 0;
	}

	template<class Y> ptr<Y> staticCast() const
	{
		return static_cast<Y*>(pObj_);
	}
	
	template<class Y> ptr<Y> dynamicCast() const
	{
		return dynamic_cast<Y*>(pObj_);
	}

	T * get() const
	{
		return pObj_;
	}

	void swap(T * & other)
	{
		T * tmp = pObj_;
		pObj_ = other;
		other = tmp;
	}
	
	void swap(ptr<T> & other)
	{
		swap(other.pObj_);
	}
private:
	T * pObj_;
};

#endif //__MCS__OBJECT__HPP
